package com.android.yinghebike.manager;

import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;

import androidx.annotation.NonNull;

import com.android.baselibrary.tool.CommToast;
import com.android.baselibrary.tool.Log;
import com.android.yinghebike.MyApplication;
import com.android.yinghebike.bean.BleDataInfo;
import com.blankj.utilcode.util.ConvertUtils;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import cn.wandersnail.ble.Connection;
import cn.wandersnail.ble.ConnectionConfiguration;
import cn.wandersnail.ble.ConnectionState;
import cn.wandersnail.ble.Device;
import cn.wandersnail.ble.EasyBLE;
import cn.wandersnail.ble.EventObserver;
import cn.wandersnail.ble.Request;
import cn.wandersnail.ble.RequestBuilder;
import cn.wandersnail.ble.RequestBuilderFactory;
import cn.wandersnail.ble.RequestType;
import cn.wandersnail.ble.WriteCharacteristicBuilder;
import cn.wandersnail.ble.WriteOptions;
import cn.wandersnail.ble.callback.NotificationChangeCallback;
import cn.wandersnail.commons.observer.Observe;
import cn.wandersnail.commons.poster.RunOn;
import cn.wandersnail.commons.poster.Tag;
import cn.wandersnail.commons.poster.ThreadMode;
import cn.wandersnail.commons.util.StringUtils;

public class BleManager implements EventObserver {

    static BleManager bleManager;

    public static BleManager getInstance() {
        if (bleManager == null) {
            bleManager = new BleManager();
            EasyBLE.getInstance().registerObserver(bleManager);
        }
        return bleManager;
    }

    String UUID_Service = "0000ffe0-0000-1000-8000-00805f9b34fb";
    String UUID_Characteristic_Notify = "0000ffe1-0000-1000-8000-00805f9b34fb";
    String UUID_Characteristic_Write = "0000ffe1-0000-1000-8000-00805f9b34fb";
    public Connection connection;
    BluetoothGattService gattService;
    BluetoothGattCharacteristic gatt_notify;
    BluetoothGattCharacteristic gatt_write;
    public Device deviceInfo;

    //连接蓝牙
    public void connect(Device info) {
        deviceInfo = info;
        //连接配置，举个例随意配置两项
        ConnectionConfiguration config = new ConnectionConfiguration();
        config.setConnectTimeoutMillis(10000);
        config.setRequestTimeoutMillis(1000);
        config.setAutoReconnect(false);
        connection = EasyBLE.getInstance().connect(info, config);//观察者监听连接状态
    }

    //断开连接
    public void disConnect() {
        if (connection != null && connection.getConnectionState() == ConnectionState.SERVICE_DISCOVERED) {
            connection.disconnect();
        }
    }

    //是否连接
    public boolean isConnect() {
        if (connection != null && connection.getConnectionState() == ConnectionState.SERVICE_DISCOVERED) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 连接状态变化回调
     */
    @Tag("onConnectionStateChanged")
    @Observe
    @RunOn(ThreadMode.MAIN)
    @Override
    public void onConnectionStateChanged(@NonNull Device device) {
        switch (device.getConnectionState()) {
            case SCANNING_FOR_RECONNECTION:
                Log.i("SCANNING_FOR_RECONNECTION...");
                break;
            case CONNECTING:
                Log.i("连接中...");
                break;
            case DISCONNECTED:
                Log.i("连接断开");
                break;
            case SERVICE_DISCOVERED:
                Log.i("连接成功");
                List<BluetoothGattService> services = connection.getGatt().getServices();
                for (BluetoothGattService service : services) {
//                    Log.i("service.getUuid():" + service.getUuid());
                    if (service.getUuid().toString().equals(UUID_Service)) {
                        gattService = service;
                    }
                    List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
                    for (BluetoothGattCharacteristic characteristic : characteristics) {
//                        Log.i("characteristic.getUuid():" + characteristic.getUuid());
                        if (characteristic.getUuid().toString().equals(UUID_Characteristic_Notify)) {
                            gatt_notify = characteristic;
                        }
                        if (characteristic.getUuid().toString().equals(UUID_Characteristic_Write)) {
                            gatt_write = characteristic;
                        }
                    }
                }
                setNotification();

                //连接成功，给蓝牙发单位指令
                int unitType = UnitManager.getUnitType();
                byte[] data = BleManager.createSendBleData((byte) 0x81, new byte[]{(byte) ((unitType) & 0xFF)});
                BleManager.getInstance().sendData(data, null);
                break;
        }
    }

    //开启通知
    private void setNotification() {
        boolean isEnabled = connection.isNotificationEnabled(gattService.getUuid(), gatt_notify.getUuid());
        Log.i("isEnabled:" + isEnabled);
        RequestBuilder<NotificationChangeCallback> builder = new RequestBuilderFactory().getSetNotificationBuilder(gattService.getUuid(), gatt_notify.getUuid(), true);
        //不设置回调，使用观察者模式接收结果
        builder.build().execute(connection);
    }

    /**
     * 通知变化回调
     */
    @Observe
    @Override
    public void onNotificationChanged(@NonNull Request request, boolean isEnabled) {
        if (request.getType() == RequestType.SET_NOTIFICATION) {
            if (isEnabled) {
                Log.d("通知开启了");
            } else {
                Log.d("通知关闭了");
            }
        } else {
            if (isEnabled) {
                Log.d("Indication开启了");
            } else {
                Log.d("Indication关闭了");
            }
        }
    }

    /**
     * 接收到数据回调
     */
    @Override
    public void onCharacteristicRead(@NonNull Request request, @NonNull byte[] value) {
        String hexStr = StringUtils.toHex(value, " ");
        Log.w("onCharacteristicRead:" + hexStr);
        parseData(value);
    }

    //发送数据
    public void sendData(byte[] data, String tag) {
        if (connection == null || connection.getConnectionState() != ConnectionState.SERVICE_DISCOVERED) {
//            CommToast.showToast(MyApplication.context, "蓝牙未连接");
            return;
        }
        Log.d("发送数据");
        WriteCharacteristicBuilder builder = new RequestBuilderFactory().getWriteCharacteristicBuilder(gattService.getUuid(), gatt_write.getUuid(), data);
        builder.setTag(tag);
        //根据需要设置写入配置
        int writeType = connection.hasProperty(gattService.getUuid(), gatt_write.getUuid(), BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) ? BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE : BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT;
        builder.setWriteOptions(new WriteOptions.Builder().setPackageSize(data.length).setPackageWriteDelayMillis(5).setRequestWriteDelayMillis(10).setWaitWriteResult(true).setWriteType(writeType).build());
        //不设置回调，使用观察者模式接收结果
        builder.build().execute(connection);
    }

    //解析数据，将数据按索引 解析成集合，方便使用
    public List<BleDataInfo> parseData(byte[] value) {
        List<BleDataInfo> dataList = new ArrayList<>();
        byte index = value[3];   //索引号
        byte length = value[1];  //数据全部总长度
        int dataLength = ConvertUtils.hexString2Int(ConvertUtils.bytes2HexString(new byte[]{length})) - 6;   //计算有效数据长度

        //循环长度，把后面的value 处理成集合装起来
        for (int i = 0; i < dataLength; i++) {
            byte dataIndex = (byte) (index + i);
            byte data = value[4 + i];
            //封装数据对象，索引号key和value
            BleDataInfo bleDataInfo = new BleDataInfo(dataIndex, data);
            dataList.add(bleDataInfo);
        }
        return dataList;
    }

    //生成查询指令   index:开始索引   length：要查询的数据长度
    public static byte[] createRequestData(byte index, int length) {
        String hex = Integer.toHexString(length);
        //组包
        byte bytes_0 = (byte) 0x5E;    //消息头
        byte bytes_1 = (byte) 0x07;              //帧长
        byte bytes_2 = (byte) 0x01;      //指令类型：0x01=查询指令。
        byte bytes_3 = index;                                //数据索引起始号
        byte[] bytes_4 = ConvertUtils.hexString2Bytes(hex);     //连续请求数据数量
        byte bytes_5 = (byte) (bytes_0 + bytes_1 + bytes_2 + bytes_3 + bytes_4[0]); //校验和
        byte bytes_6 = (byte) 0x0D;

        byte[] arr = {bytes_0, bytes_1, bytes_2, bytes_3, bytes_4[0], bytes_5, bytes_6};
        return arr;
    }

    //生成控制指令   index:命令对应的索引
    //0x01=应答确认成功（指令接收正常）。
    //0x02=应答确认失败（指令接收有误，或从机暂时不能执行该指令）。
    //0x03=关机指令
    //0x04=开机指令
    //0x05=使仪表进入导航界面。
    //0x06=使仪表进入骑行界面（主界面）。APP 登录后，可发送此指令，使仪表进入主界面。如果仪表端正处
    //于密码输入界面，则仪表直接跳过密码进入主界面。
    //0xEA=使仪表进入升级状态。
    public static byte[] createControlData(byte index) {
        //组包
        byte bytes_0 = (byte) 0x5E;    //消息头
        byte bytes_1 = (byte) 0x06;       //帧长
        byte bytes_2 = (byte) 0x03;      //指令类型：0x03=控制指令
        byte bytes_3 = index;            //指令
        byte bytes_4 = (byte) (bytes_0 + bytes_1 + bytes_2 + bytes_3); //校验和
        byte bytes_5 = (byte) 0x0D;

        byte[] arr = {bytes_0, bytes_1, bytes_2, bytes_3, bytes_4, bytes_5};
        return arr;
    }

    //生成发送数据包  index:开始索引    data：数据包
    public static byte[] createSendBleData(byte index, byte[] data) {
        int length = 6 + data.length;
        //组包
        byte bytes_0 = (byte) 0x5E;      //消息头
        byte bytes_1 = (byte) (length & 0xFF);      //数据总长度
        byte bytes_2 = (byte) 0x02;      //指令类型：0x01=查询指令。
        byte bytes_3 = index;      //数据索引起始号

        byte bytes_5 = (byte) (bytes_0 + bytes_1 + bytes_2 + bytes_3); //校验和
        for (byte item : data) {
            bytes_5 = (byte) (bytes_5 + item);
        }
        byte bytes_6 = (byte) 0x0D;

        //组成最终包
        ByteBuffer byteBuffer_final = ByteBuffer.allocate(length);
        byteBuffer_final.put(bytes_0);
        byteBuffer_final.put(bytes_1);
        byteBuffer_final.put(bytes_2);
        byteBuffer_final.put(bytes_3);
        byteBuffer_final.put(data);
        byteBuffer_final.put(bytes_5);
        byteBuffer_final.put(bytes_6);
        byte[] bytes_final = byteBuffer_final.array();
        return bytes_final;
    }



}
